package buildtools;

import java.util.List;
import java.util.Set;

import javax.media.j3d.Transform3D;
import javax.vecmath.AxisAngle4d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import math.Angles;
import math.Precision;
import math.Spherical;

import component.AbstractComponent;
import component.Ball;
import component.ComponentFactory;
import component.Status;
import component.Structure;
import component.Tube;
import component.group.ComponentGroup;

public class CircleBuilder extends StructureBuilder {

	private Ball commonBall = null;
	private List<Tube> tubes = null;
	private Point3d[] tubeEnds = null;
	private Vector3d normal = null;
	private Vector3d v1=null, v2=null;
	
	public CircleBuilder(Structure structure, ComponentFactory factory) {
		super(structure, factory);
		
	}

	public void makeStructureFromGroup(List<Tube> begin) {
		this.clearTemporary();
		commonBall = null;
		tubes = begin;
		if (tubes.size() != 2)
			return;
		
		double angle = this.getAngleBetween(tubes.get(0), tubes.get(1));
		System.out.println("Angle found: " + Math.toDegrees(angle));
		double constructionAngle = 0;
		for (int i=3; i<=12; i++) {
			constructionAngle = Angles.getAngle(i);
			if (Precision.compare(constructionAngle, angle)) {
				System.out.println("Construction angle: " + Math.toDegrees(constructionAngle));
				System.out.println("N-kant: " + i);
				makeCircle(i);
			}
		}
		
	}
	
	private void makeCircle(int n) {
		double constructionAngle = Angles.getAngle(n);
		AxisAngle4d axisAngle = new AxisAngle4d(normal, Math.PI - constructionAngle);
		Transform3D transform = new Transform3D();
		transform.set(axisAngle);
		
		Point3d currentPos = new Point3d(tubeEnds[0]);
		Vector3d nextPos = new Vector3d(v1);
		
		this.temporaryGroup = new ComponentGroup();
		
		for (int i=0; i<n-2; i++) {
			transform.transform(nextPos);
			placeBallAt(currentPos);
			placeTubeAt(currentPos, nextPos);
			currentPos.add(nextPos);
		}
		
		placeBallAt(currentPos);
		
		this.temporaryGroup.setStatus(Status.PLACEMENT | Status.TRANSPARENT);
	}
	
	private void placeBallAt(Point3d pos) {
		Ball ball = this.findBallAt(pos);
		if (ball != null)
			return;
		
		ball = factory.createBall(pos);
		ball.setStatus(Status.INVISIBLE);
		structure.addTemporaryComponent(ball);
		if (!ball.isValidPosition()) {
			ball.dispose();
		} else {
			temporaryGroup.add(ball);
		}
	}
	
	private void placeTubeAt(Point3d pos, Vector3d next) {
		Point3d nextPos = new Point3d(pos);
		nextPos.add(next);
		Tube tube = this.findTubeAt(pos, nextPos);
		
		if (tube != null)
			return;
		
		Spherical orientation = new Spherical();
		orientation.setVector(next);
		
		tube = factory.createTube(pos, orientation);
		tube.setStatus(Status.INVISIBLE);
		structure.addTemporaryComponent(tube);
		if (!tube.isValidPosition()) {
			tube.dispose();
		} else {
			temporaryGroup.add(tube);
		}
	}

	private double getAngleBetween(Tube t1, Tube t2) {
		Set<AbstractComponent> connected = t1.getConnectedComponents();
		commonBall = null;
		for (AbstractComponent c : connected) {
			if (t2.isConnectedTo(c) && c instanceof Ball) {
				commonBall = (Ball)c;
			}
		}
		if (commonBall == null)
			return 0;
		
		tubeEnds = new Point3d[2];
		
		v1 = getVectorFrom(commonBall.getPos(), t1, 0);
		v2 = getVectorFrom(commonBall.getPos(), t2, 1);
		
		normal = new Vector3d();
		normal.cross(v1, v2);
		
		return v1.angle(v2);
	}
	
	private Vector3d getVectorFrom(Point3d p, Tube t, int nr) {
		Vector3d orientation = t.getOrientation().getVector();
		if (Precision.compare(p, t.getSecondaryPosition())) {
			orientation.negate();
			tubeEnds[nr] = t.getPos();
		} else {
			tubeEnds[nr] = t.getSecondaryPosition();
		}
		return orientation;
	}
	@Override
	public void temporaryClicked(AbstractComponent component) {
		for (AbstractComponent c : temporaryGroup.getComponents()) {
			structure.transferToPermanent(c);
		}
		temporaryGroup.setStatus(Status.NORMAL);
		temporaryGroup.clear();
		clearTemporary();
		connector.connectAllPossible();
	}
	
	@Override
	public void clearTemporary() {
		super.clearTemporary();
		if (this.tubes != null) {
			tubes.clear();
			tubes = null;
		}
		commonBall = null;
		tubeEnds = null;
	}

}
